#include "CSVFile.h"

// Contrói uma interface para um arquivo CSV passando seu nome e o tipo (input ou output)
CSVFile::CSVFile(const char *filename, int type)
{
    this->type = type;

    // Input
    if(type == CSV_IN)
    {
        in = new ifstream(filename);
        assert(in->is_open());

        getChar();
        goToNextNumber();
    }
    // Output
    else
    {
        out = new ofstream(filename);
        assert(out->is_open());
        bol = true;
    }
}

CSVFile::~CSVFile()
{
    if(type == CSV_IN)
        delete in;
    else
        delete out;
}

// Verifica se o caracter faz parte do número
bool CSVFile::isNumber()
{
    return isdigit(c) || c == '.' || c == '-';
}

// Lê um caracter do arquivo
void CSVFile::getChar()
{
    in->read(&c, sizeof(char));
    eof = in->eof();
    if(eof)
        c = 0;
}

// Lê caracteres até encontrar um número
void CSVFile::goToNextNumber()
{
    while(!eof && !isNumber())
        getChar();
}

// Retorna verdadeiro se tiver dados a serem lidos
bool CSVFile::has()
{
    return !eof;
}

// Lê os valores de uma linha e os coloca no vetor de entrada
void CSVFile::read(vdouble& values)
{
    uint state = CAPTURE_NUMBER;
    string str;

    while(state != END)
    {
        switch(state)
        {
            case CAPTURE_NUMBER: // Capturando o número
                if(isNumber())
                {
                    str += c;
                    getChar();
                }
                else
                {
                    double d = atof(str.c_str());
                    values.push_back(d);
                    str.clear();

                    if(eof || c == '\n')
                        state = GOTO_NEXT_NUMBER;
                    else
                        state = ELIMINATE_SPACES;
                }
                break;

            case 1: // Eliminando os espaços depois do número
                if(eof || c == '\n')
                    state = GOTO_NEXT_NUMBER;
                else if(isNumber())
                    state = CAPTURE_NUMBER;
                else
                    getChar();
                break;

            case 2: // Parando no próximo número
                goToNextNumber();
                state = END;
                break;
        }
    }

    assert(values.is_full());
}

// Escreve um valor no arquivo
void CSVFile::write(double value)
{
    if(!bol)
        (*out) << ",";
    (*out) << value;
    bol = false;
}

// Escreve um valor no arquivo
void CSVFile::write(uint& value)
{
    if(!bol)
        (*out) << ",";
    (*out) << value;
    bol = false;
}

// Escreve um vetor de valores no arquivo
void CSVFile::write(vdouble& values)
{
    for(uint i = 0; i < values.size(); i++)
        write(values.get(i));
}

// Escreve um '\n' no arquivo
void CSVFile::wendl()
{
    (*out) << endl;
    bol = true;
}

